/*
 *  CC3PerformanceWorld.cpp
 *  cocos3d-x
 *
 *  Created by Yezhiyong on 12-4-24.
 *  Copyright 2012 __MyCompanyName__. All rights reserved.
 *
 */

#include "CC3PerformanceWorld.h"
#include "CC3Camera.h"
#include "CC3Light.h"
#include "NodeGrid.h"
#include "CC3MeshNode.h"
#include "CC3Texture.h"
#include "CC3Math.h"


CC3PerformanceWorld::CC3PerformanceWorld() :
	templateNode(NULL),
	perSideCount(1)
{
}

CC3PerformanceWorld::~CC3PerformanceWorld()
{
	if (availableTemplateNodes) 
	{
		delete availableTemplateNodes;
		availableTemplateNodes = NULL;
	}
}

CC3PerformanceWorld* CC3PerformanceWorld::world()
{
	CC3PerformanceWorld* pWorld = new CC3PerformanceWorld;
	if (pWorld) 
	{
		// 如果构造函数里不实现多态,需要再调用
		pWorld->initWithTag(pWorld->nextTag(), NULL);
		pWorld->autorelease();
	}
	
	return pWorld;
}

void CC3PerformanceWorld::initializeWorld()
{
	shouldAnimateNodes = false;
	
	// Create the camera, place it back a bit, and add it to the world
	CC3Camera* cam = CC3Camera::nodeWithName("Camera");
	cam->setLocation(cc3v( 0.0, 100.0, 200.0 ));
	cam->setTargetLocation(kCC3VectorZero);
	addChild(cam);
	
	// Create a light, place it back and to the left at a specific
	// position (not just directional lighting), and add it to the world
	CC3Light* lamp = CC3Light::nodeWithName("Lamp");
	lamp->setLocation(cc3v( -20.0, 0.0, 0.0 ));
	lamp->setIsDirectionalOnly(false);
	cam->addChild(lamp);
	
	nodeGrid = NodeGrid::nodeWithName("NodeGrid");
	addChild(nodeGrid);
	
	availableTemplateNodes = new CCMutableArray<CC3Node*>(8);
	
	// Make a simple plane template available. Only 2 faces per node.
	CC3MeshNode* aNode = CC3PlaneNode::nodeWithName("Simple plane");
	aNode->populateAsCenteredRectangleWithSizeWithTextureAndInvertTexture(CCSizeMake(30.0, 30.0),
											   CC3Texture::textureFromFile("Icon@2x.png"),
											   true);
	availableTemplateNodes->addObject(aNode);
	
	setTemplateNode((CC3Node*)(availableTemplateNodes->getObjectAtIndex(0)));
	setPerSideCount(1);
}

void CC3PerformanceWorld::updateBeforeTransform(CC3NodeUpdatingVisitor* visitor)
{
	updateCameraFromControls(visitor->getDeltaTime());
}

void CC3PerformanceWorld::updateCameraFromControls(ccTime dt)
{
}

void CC3PerformanceWorld::layoutGrid()
{
	nodeGrid->populateWith(templateNode, perSideCount);
}

CC3NodeUpdatingVisitor* CC3PerformanceWorld::updateVisitorClass(ccTime dt)
{
	return shouldAnimateNodes ? CC3AnimatingVisitor::visitorWithDeltaTime(dt) : CC3World::updateVisitorClass(dt);
}

CC3Node* CC3PerformanceWorld::getTemplateNode()
{
	return templateNode;
}

void CC3PerformanceWorld::setTemplateNode(CC3Node * aNode)
{
	CC3Node* oldNode = templateNode;
	templateNode = aNode;
	templateNode->retain();
	if (oldNode) 
	{
		oldNode->release();
	}
	layoutGrid();
}

/**
 * When the quantity of nodes is changed, layout the grid
 * with the new number of copies of the template node.
 */
void CC3PerformanceWorld::setPerSideCount(unsigned int aCount)
{
	perSideCount = aCount;
	layoutGrid();
}

/////////////////////////////////////////////////////

bool CC3AnimatingVisitor::nodeIsFromTemplate(CC3Node* aNode)
{
	return (aNode->getName() == ((CC3PerformanceWorld*)getStartingNode())->getTemplateNode()->getName());
}

/**
 * If the node is one of the copies of the template node, rotate it a bit.
 * The rotation direction and magnitude is derived from the node's unique tag,
 * so that each template copy rotates a little differently than any of the others.
 */
void CC3AnimatingVisitor::processBeforeChildren(CC3Node* aNode)
{
	if (nodeIsFromTemplate(aNode)) 
	{
		GLfloat direction = SIGN((GLfloat)(aNode->getTag() % 2) - 0.5);
		GLfloat magnitude = (GLfloat)(aNode->getTag() % 8) + 4;
		CC3Vector deltaRot = cc3v(direction * magnitude * 5.0 * deltaTime,
								  direction * magnitude * 7.0 * deltaTime,
								  0.0);
		aNode->setRotation(CC3VectorAdd(aNode->getRotation(), deltaRot));
	}
	CC3NodeUpdatingVisitor::processBeforeChildren(aNode);
}

CC3AnimatingVisitor* CC3AnimatingVisitor::visitorWithDeltaTime(ccTime dt)
{
	CC3AnimatingVisitor* pVisitor = new CC3AnimatingVisitor;
	if (pVisitor) 
	{
		pVisitor->initWithDeltaTime(dt);
		pVisitor->autorelease();
	}
	
	return pVisitor;
}

